﻿using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Reflection;

namespace MiniFox.Data
{
    /// <summary>
    /// 
    /// </summary>
    public class XDataTable : DataTable
    {
        /// <summary>
        /// 
        /// </summary>
        public XDataTable() : base()
        {
            this.BeginInit();
            this.EndInit();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="tableName"></param>
        public XDataTable(string tableName) : base(tableName)
        {
            this.BeginInit();
            this.EndInit();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="tableNamespace"></param>
        public XDataTable(string tableName, string tableNamespace) : base(tableName, tableNamespace)
        {
            this.BeginInit();
            this.EndInit();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected override DataTable CreateInstance()
        {
            return new XDataTable();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public new XDataRow NewRow()
        {
            return ((XDataRow)(base.NewRow()));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataRow"></param>
        public void AddRow(XDataRow dataRow)
        {
            this.Rows.Add(dataRow);
            dataRow.RowIndex = this.Rows.Count;
            dataRow.SetAdded();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="rowData"></param>
        public XDataRow AddRow(IDictionary<string, object> rowData)
        {
            var dataRow = this.NewRow();
            foreach(var kv in rowData)
            {
                dataRow[kv.Key] = kv.Value;
            }
            this.AddRow(dataRow);
            return dataRow;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        protected override DataRow NewRowFromBuilder(DataRowBuilder builder)
        {
            return new XDataRow(builder);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected override Type GetRowType()
        {
            return typeof(XDataRow);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="rowIndex"></param>
        public XDataRow SetRowDelete(int rowIndex)
        {
            var row = (XDataRow)this.Rows[rowIndex];
            row.Delete();
            return row;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="rowIndex"></param>
        /// <returns></returns>
        public XDataRow SetRowChanged(int rowIndex)
        {
            var row = (XDataRow)this.Rows[rowIndex];
            row.SetModified();
            return row;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class XDataTable<T> : DataTable
    {
        /// <summary>
        /// 
        /// </summary>
        public XDataTable() : base()
        {
            this.BeginInit();
            this.InitClass();
            this.EndInit();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="tableName"></param>
        public XDataTable(string tableName) : base(tableName)
        {
            this.BeginInit();
            this.InitClass();
            this.EndInit();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected override DataTable CreateInstance()
        {
            return new XDataTable<T>();
        }

        private void InitClass()
        {
            List<DataColumn> pks = null;
            Type type = typeof(T);
            this.Namespace = type.Namespace;
            if (string.IsNullOrWhiteSpace(this.TableName))
            {
                TableAttribute tableAtt = Attribute.GetCustomAttribute(type, typeof(TableAttribute)) as TableAttribute;
                string tableName = (tableAtt == null || string.IsNullOrWhiteSpace(tableAtt.Name)) ? type.Name : tableAtt.Name;
                this.TableName = tableName;
            }

            foreach (PropertyInfo property in type.GetProperties())
            {
                ColumnAttribute colAtt = property.GetCustomAttribute<ColumnAttribute>();
                if (colAtt == null)
                    continue;

                DataColumn col = new DataColumn(colAtt.Name, property.PropertyType);
                col.SetOrdinal(colAtt.Order);
                this.Columns.Add(col);

                DisplayAttribute displayAttribute = property.GetCustomAttribute<DisplayAttribute>();
                if (displayAttribute != null)
                {
                    col.Caption = displayAttribute.ShortName ?? displayAttribute.Name;
                }
                KeyAttribute keyAtt = property.GetCustomAttribute<KeyAttribute>();

                if ((keyAtt != null))
                {
                    if (pks == null)
                        pks = new List<DataColumn>();
                    pks.Add(col);
                }
                EditableAttribute editableAttribute = property.GetCustomAttribute<EditableAttribute>();
                col.ReadOnly = (editableAttribute != null && !editableAttribute.AllowEdit);

                //自增字段
                IncrementSequenceAttribute autoIncrement = Attribute.GetCustomAttribute(property, typeof(IncrementSequenceAttribute)) as IncrementSequenceAttribute;
                if (autoIncrement != null)
                {
                    col.AutoIncrement = true;
                    col.AutoIncrementSeed = autoIncrement.Seed;
                    col.AutoIncrementStep = autoIncrement.Step;
                }
            }
            this.PrimaryKey = pks.ToArray();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public new XDataRow<T> NewRow()
        {
            return ((XDataRow<T>)(base.NewRow()));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        protected override DataRow NewRowFromBuilder(DataRowBuilder builder)
        {
            return new XDataRow<T>(builder);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected override Type GetRowType()
        {
            return typeof(XDataRow<T>);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataTable"></param>
        /// <returns></returns>
        public static explicit operator List<T>(XDataTable<T> dataTable)
        {
            if (dataTable == null)
                return null;
            List<T> list = new List<T>();
            foreach (DataRow row in dataTable.Rows)
            {
                PropertyInfo[] properties = typeof(T).GetProperties();
                T entity = (T)(XDataRow<T>)row;
                list.Add(entity);
            }
            return list;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public static implicit operator XDataTable<T>(List<T> entities)
        {
            if (entities == null)
                return null;

            XDataTable<T> table = new XDataTable<T>();
            Type type = typeof(T);
            PropertyInfo[] properties = type.GetProperties();

            foreach (T entity in entities)
            {
                XDataRow<T> row = table.NewRow();

                foreach (PropertyInfo property in properties)
                {
                    ColumnAttribute colAtt = Attribute.GetCustomAttribute(property, typeof(ColumnAttribute)) as ColumnAttribute;
                    if (colAtt == null)
                        continue;
                    object Val = property.GetValue(entity);
                    if (property.PropertyType.IsClass && Val == null)
                    {
                        Val = System.DBNull.Value;
                    }

                    row[colAtt.Name] = Val;
                }
                table.Rows.Add(row);
                row.AcceptChanges();

            }
            return table;
        }

    }

}
